
import java.util.*;

/**
 * Clase encargada de la decodificacion de los datos.
 */
class Decode extends Stage {

    private Vector hazardList;
    private Instruction instructionSave;
    private int savePC;
    public boolean isStall;
    public boolean stallflag;
    public boolean assumeBranchTaken;
    public boolean branchPrediction;
    public boolean forwarding;
    public Vector branchTable;

    public Decode() {
        super();
        instructionSave = new Instruction("NOP");
        isStall = false;
        assumeBranchTaken = true;   //asume que las ramas siempre son tomadas.
        hazardList = new Vector(3);
        branchTable = new Vector();
        hazardList.addElement(new Integer(0));
        hazardList.addElement(new Integer(0));
        hazardList.addElement(new Integer(0));
    }

    /**
     * Procesa la instruccion actual. Implementa la funcionalidad de la etapa de
     * decodificación.
     *
     * @see Instruction
     * @see Fetch
     * @see Execute
     * @see Memory
     * @see WriteBack
     * @see MemoryType
     * @see Listboxes
     */
    public void step(Vector Instructions, MemoryType regFile,
            Memory myMemory, WriteBack myWriteBack,
            Fetch myFetch, Execute myExecute, ListBoxes lb) {

        String str;

        if (PC == Instructions.size()) {
            PC = -9;
        }
        if (isStall == true) {
            myInstruction = instructionSave;
            PC = savePC;
        }

        stallflag = false;
        if ((((myExecute.myInstruction.opcode != 0) &&
                (myExecute.myInstruction.flush == false) &&
                (myInstruction.rs ==
                ((Integer) hazardList.elementAt(0)).intValue())) ||
                ((myMemory.myInstruction.opcode != 0) &&
                (myMemory.myInstruction.flush == false) &&
                (myInstruction.rs ==
                ((Integer) hazardList.elementAt(1)).intValue())) ||
                ((myWriteBack.myInstruction.opcode != 0) &&
                (myWriteBack.myInstruction.flush == false) &&
                (myInstruction.rs ==
                ((Integer) hazardList.elementAt(2)).intValue()))) &&
                (myInstruction.opcode != 0) && (myInstruction.flush == false)) {
            stallflag = true;
        }

        if ((forwarding == true) && (stallflag == true)) {
            if ((myInstruction.rs == myMemory.myInstruction.rd) &&
                    (myInstruction.rs != myExecute.myInstruction.rd)) {
                switch (myMemory.myInstruction.opcode) {
                    case 0:   // NOP
                    case 35:  // LW
                    case 43:  // SW
                    case 70:  // BEQ
                    case 71:  // BNE
                        break;      // ignorar estas instrucciones.
                    default:
                        myInstruction.rsValue = myMemory.myInstruction.rdValue;
                        stallflag = false;
                        myInstruction.forwardRsFlag = true;
                        str = "Resultultado del forwarded del ALU: $" + myInstruction.rs + "=" +
                                myInstruction.rsValue + ".";
                        lb.Messages.add(str, 0);
                }
            } else if ((myInstruction.rs == myWriteBack.myInstruction.rd) &&
                    (myInstruction.rs != myExecute.myInstruction.rd)) {
                myInstruction.rsValue = myWriteBack.myInstruction.rdValue;
                stallflag = false;
                myInstruction.forwardRsFlag = true;
                str = "Resultado del forwarded de la MEMORIA: $" + myInstruction.rs + "=" +
                        myInstruction.rsValue + ".";
                lb.Messages.add(str, 0);
            }
        }

        if ((myInstruction.isImmediate() == false) && (myInstruction.opcode != 35) &&
                (((myExecute.myInstruction.opcode != 0) &&
                (myExecute.myInstruction.flush == false) &&
                (myInstruction.rt ==
                ((Integer) hazardList.elementAt(0)).intValue())) ||
                ((myMemory.myInstruction.opcode != 0) &&
                (myMemory.myInstruction.flush == false) &&
                (myInstruction.rt ==
                ((Integer) hazardList.elementAt(1)).intValue())) ||
                ((myWriteBack.myInstruction.opcode != 0) &&
                (myWriteBack.myInstruction.flush == false) &&
                (myInstruction.rt ==
                ((Integer) hazardList.elementAt(2)).intValue()))) &&
                (myInstruction.opcode != 0) && (myInstruction.flush == false)) {
            stallflag = true;
        }


        if ((!myInstruction.isImmediate()) && (forwarding == true) && (stallflag == true)) {
            // see if value ready at end of last execute stage (now memory stage)
            if ((myInstruction.rt == myMemory.myInstruction.rd) &&
                    (myInstruction.rt != myExecute.myInstruction.rd)) {
                switch (myMemory.myInstruction.opcode) {
                    case 0:   // NOP
                    case 35:  // LW
                    case 43:  // SW
                    case 70:  // BEQ
                    case 71:  // BNE
                        break;      // ignorar estas instrucciones.
                    default:
                        myInstruction.rtValue = myMemory.myInstruction.rdValue;
                        stallflag = false;
                        myInstruction.forwardRtFlag = true;
                        str = "Resultado del forwarded del ALU: $" + myInstruction.rt + "=" +
                                myInstruction.rtValue + ".";
                        lb.Messages.add(str, 0);
                }
            } else if ((myInstruction.rt == myWriteBack.myInstruction.rd) &&
                    (myInstruction.rt != myExecute.myInstruction.rd)) {
                myInstruction.rtValue = myWriteBack.myInstruction.rdValue;
                stallflag = false;
                myInstruction.forwardRtFlag = true;
                str = "Resultado del forwarded del MEMORIA: $" + myInstruction.rt + "=" +
                        myInstruction.rtValue + ".";
                lb.Messages.add(str, 0);

            }
        }


        if (stallflag == true) {

            if (isStall == false) {
                isStall = true;
                instructionSave = myInstruction;
                savePC = PC;
            }
            myInstruction = new Instruction("NOP");
            myInstruction.rsValue = 0;
            myInstruction.rtValue = 0;
            PC = -9;
        } else {

            if (isStall == true) {
                isStall = false;
                myInstruction = instructionSave;
            }


        }

        if (myInstruction.forwardRsFlag == false) {
            myInstruction.rsValue = regFile.getValue(myInstruction.rs);
        }
        if (myInstruction.forwardRtFlag == false) {
            if (myInstruction.isImmediate()) {
                myInstruction.rtValue = myInstruction.immediate;
            } else {
                myInstruction.rtValue = regFile.getValue(myInstruction.rt);
            }
        }

        hazardList.setElementAt(hazardList.elementAt(1), 2);
        hazardList.setElementAt(hazardList.elementAt(0), 1);
        hazardList.setElementAt(new Integer(myInstruction.rd), 0);

        if (isStall == true) {
            str = "Bloqueos emitidos para las instrucciones" + savePC + ".";
            lb.Messages.add(str, 0);
        }
    }

    /**
     * Regresa una representacion de la actual etapa de instruccion y el resultado
     * correspondiente.
     */
    public String toString() {
        String temp;

        if (myInstruction.flush == true) {
            temp = "FLUSHED: \n" + myInstruction + "\n";
            return temp;
        }

        if (PC >= 0) {
            temp = Integer.toString(PC) + ":\n" + myInstruction + "\n" +
                    "ALUop1= " + Integer.toString(myInstruction.rsValue) + "\n" +
                    "ALUop2= " + Integer.toString(myInstruction.rtValue) + "\n";
        } else {
            temp = myInstruction + "\n";
            if (isStall) {
                temp += "Bloqueo:\n" + Integer.toString(savePC) + ":  " +
                        instructionSave + "\n";
            }
        }

        return temp;
    }
}